package com.example.demo;

import cn.hutool.core.util.ObjUtil;
import com.example.demo.config.SecurityConfig;
import com.example.demo.constant.SecurityConstant;
import com.example.demo.util.JwtUtil;
import com.example.demo.util.RedisUtil;
import com.example.demo.vo.Result;
import com.example.demo.vo.ResultCode;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.jsonwebtoken.Claims;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;


import java.io.IOException;


/**
 * Jwt 权限认证过滤器
 *
 * @author star
 */
@Component
@AllArgsConstructor
public class JwtTokenFilter implements Filter {
    private JwtUtil jwtUtil;
    private ObjectMapper objectMapper;
    private RedisUtil redisUtil;
    private AntPathMatcher antPathMatcher;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        //因为会触发两次过滤:第一次是有请求调用时，会触发security的过滤； 第二次是因为业务中有获取请求头并且使用请求头的代码，会触发apache的过滤
        // 1、是否已经认证过
        if (ObjUtil.isNotEmpty(SecurityContextHolder.getContext().getAuthentication())) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }


        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("application/json;charset=utf-8");

        // 2、如果是白名单页则跳过
        for (String white: SecurityConfig.WHITE_LIST) {
            if (antPathMatcher.match(white,request.getRequestURI())){
                filterChain.doFilter(request,response);
                return;
            }
        }

        // 3、获取请求头中的jwt
        String jwtToken = request.getHeader(SecurityConstant.TOKEN_HEADER);
        if (ObjUtil.isEmpty(jwtToken)) {
            response.getWriter().write(objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0305)));
            return;
        }

        // 4、解析 JwtToken
        String username = null;
        try {
            Claims claims = jwtUtil.parse(jwtToken);
            username = claims.getSubject();
            //将用户名存在请求对象中
            request.setAttribute("username",username);
        } catch (Exception e) {
            //无效的凭证
            response.getWriter().write(objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0304)));
            return;
        }

        // 5、从 Redis 中取出 信息，并强转为 authentication
        Authentication authentication = (Authentication) redisUtil.hashGet(SecurityConstant.TOKEN_KEY, username);
        if (ObjUtil.isEmpty(authentication)) {
            //凭证授权已经过期
            response.getWriter().write(objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0311)));
            return;
        }

        // authentication 有效，将其交给 Security，认证成功
        //通过SecurityContextHolder拿到上下文，将给定的身份验证对象（authentication）设置为当前线程的身份验证对象
        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(request,response);
    }

}
